package com.dongshan.myregistry.cluster;

import com.dongshan.myregistry.conf.MyRegisterConfigProperties;
import com.dongshan.myregistry.http.HttpInvoker;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 注册中心的集群
 */
public class Cluster {

    private MyRegisterConfigProperties myRegisterConfigProperties;
    private List<Server> serverList;
    private long delay = 5_000;
    private Server MYSELF;
    @Value("${server.port:8383}")
    private Integer port;
    private String host;
    private Logger log = LoggerFactory.getLogger(Cluster.class);
    // 探活任务
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();


    public Cluster(MyRegisterConfigProperties myRegisterConfigProperties) {
        this.myRegisterConfigProperties = myRegisterConfigProperties;
    }

    public void init(){
        //1.获取host
        try(InetUtils inetUtils = new InetUtils(new InetUtilsProperties())){
            this.host = inetUtils.findFirstNonLoopbackAddress().getHostAddress();
        }catch(Exception e){
            this.host = "127.0.0.1";
        }
        this.MYSELF = new Server(Strings.lenientFormat("http://%s:%s",this.host,port),true,false,-1L);
        //2.获取server列表
        List<Server> servers = Lists.newArrayList();
        for (String url : myRegisterConfigProperties.getServerList()) {
            if(url.contains("localhost")){
                url = url.replace("localhost",this.host);
            }else if(url.contains("127.0.0.1")){
                url = url.replace("127.0.0.1",this.host);
            }
            //如果服务是自己
            if(url.equals(this.MYSELF.getUrl())){
                servers.add(this.MYSELF);
            }else{
                Server server = new Server();
                server.setUrl(url);
                server.setStatus(false);
                server.setLeader(false);
                server.setVersion(-1L);
                servers.add(server);
            }

        }
        this.serverList = servers;
        // 探活
        executor.scheduleWithFixedDelay(()->{
            try{
                // 探活
                checkRegisterAlive();
                // 选主
                selectLeader();
            }catch (Exception e){
                log.error(Throwables.getStackTraceAsString(e));
            }
        },0,delay, TimeUnit.MILLISECONDS);
    }

    /**
     * 选主
     */
    private void selectLeader() {
        List<Server> leaderList = serverList.stream().filter(Server::getStatus).filter(Server::getLeader).toList();
        if(leaderList.isEmpty()){
            log.debug("===> ********select leader for no leader");
            select();
        }else if(leaderList.size() > 1){
            log.debug("===> +++++ select for more one leader");
            select();
        }else{
            log.debug("===> already hava a leader!");
        }
    }

    private void select() {
        Server candidate = null;
        for (Server server : serverList) {
            server.setLeader(false);
            // 对于存活的服务列表，选取候选人
            if(server.getStatus()){
                // 候选人不能为空
                if(Objects.isNull(candidate)){
                    candidate = server;
                // 为保证每次选出来的候选人是稳定的，我们通过哈希code，每次把hashcode较小的server作为候选人
                }else if(candidate.hashCode() > server.hashCode()){
                    candidate = server;
                }
            }
        }
        // 将候选人设置为leader
        if(!Objects.isNull(candidate)){
            candidate.setLeader(true);
        }
    }

    private void checkRegisterAlive(){
        for (Server server : serverList) {
            try{
                Server info = HttpInvoker.getHttp(Strings.lenientFormat("%s/myregistercontroller/info", server.getUrl()), Server.class);
                if(Objects.nonNull(info)){
                    server.setStatus(true);
                    server.setLeader(info.getLeader());
                    server.setVersion(info.getVersion());
                }else{
                    server.setStatus(false);
                    server.setLeader(false);
                }
            }catch(Exception e){
                server.setStatus(false);
                server.setLeader(false);
            }
        }
    }

    public Server self(){
        return this.MYSELF;
    }

    public Server leader(){
        return serverList.stream().filter(Server::getStatus).filter(Server::getLeader).findAny().orElse(null);
    }

    public List<Server> getServerList() {
        return serverList;
    }
}
